package rs.etf.kdp.workstation;

import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.PriorityQueue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


import rs.ac.bg.etf.sleep.simulation.Event;
import rs.ac.bg.etf.sleep.simulation.SimBuffer;
import rs.etf.kdp.server.EventFromWS;
import rs.etf.kdp.server.Part;

public class PrioritySimBuffer<V> implements SimBuffer<V> {

	private ObjectOutputStream output;
	private Part myPart;
	private HashMap<Long, Boolean> presentComponents;
	private PriorityQueue<Event<V>> queue;
	int inBuffer=0;
	private final Lock lock = new ReentrantLock();
	//private final Condition full = lock.newCondition();
	private final Condition empty = lock.newCondition();
	
	public PrioritySimBuffer(Part myPart, ObjectOutputStream output)
	{
		this.output = output;
		this.myPart = myPart;
		this.queue = new PriorityQueue<Event<V>>();
		presentComponents = new HashMap<Long, Boolean>();
		List<String> componentsList = myPart.getComponents();
		
		//put all components in hash map
		for(String s : componentsList)
		{
			int firstSpace = s.indexOf(' ');
			String charId = s.substring(0, firstSpace);
			Long id = null;
			try {
				id = Long.parseLong(charId);
			} catch(NumberFormatException ne)
			{
				System.out.println("Number format exception in SimBufferLocal");
			}
			
			presentComponents.put(id, true);
		}
	}
	
	@Override
	public synchronized void putEvent(Event<V> event) {
		Long destID = event.getDstID();
		boolean isLocal;
		if(presentComponents.get(destID)!=null)
		{
			isLocal = true;
		} else
		{
			isLocal = false;
		}
		
		//determine weather put it in local buffer or sent it via socket
		if(isLocal)
		{
			putInLocalBuffer(event);
		} else
		{
			sendToOtherWorkstation(event);
		}
		
	}
	
	public synchronized void putInLocalBuffer(Event<V> event)
	{
		lock.lock();
		try
		{
			queue.add(event);
			inBuffer++;
			empty.signal();
		} finally
		{
			lock.unlock();
		}
	}
	
	private synchronized void sendToOtherWorkstation(Event<V> event)
	{
		EventFromWS wsEvent = new EventFromWS(event, null);
		String message = "workstation transport " + event.getDstID() + " " + myPart.getServerId();
		
		try {
			output.writeObject(message);
			output.writeObject(wsEvent);
		} catch (IOException e) {
			System.out.println("Unable to write object in sentToOtherWorkstation");
		}
		
	}

	@Override
	public void putEvents(List<Event<V>> events) {
		for (Event<V> event : events) {
			putEvent(event);
		}
	}

	@Override
	public Event<V> getEvent() {
		Event<V> res = null;
		lock.lock();

		try
		{
			while(inBuffer==0)
			{
				//System.out.println("Sad ce da se blokira");
				try {
					empty.await();
				} catch (InterruptedException e) {
					System.out.println("Interrupted on wait on empty buffer");
				}
				//System.out.println("Odblokirao se");
			}
			res =  queue.remove();
			inBuffer--;
		} finally
		{
			lock.unlock();
		}
		return res;
	}

	@Override
	public List<Event<V>> getEvents() {
		List<Event<V>> list = new LinkedList<Event<V>>();
		list.add(getEvent());
		return list;
	}

	@Override
	public boolean isEmpty() {
		return queue.isEmpty();
	}

	@Override
	public long getMinrank() {
		return 0;
	}

}
